package model

import (
	"gitee.com/sansaniot/ssiot-core/facade/env"
	"gitee.com/sansaniot/ssiot-core/httpmvc/model"
	"ssdevice/common/constant"
	"ssdevice/common/dto"
	"ssdevice/internal/rule/model/query"
)

type Processor struct {
	Id         string        `json:"id"`
	Name       string        `json:"name"`
	Script     string        `json:"script"`
	Next       string        `json:"next"`
	Previous   string        `json:"previous"`
	RuleId     string        `json:"ruleId"`
	Desc       string        `json:"desc"`
	Loc        string        `json:"loc"`
	Schedule   string        `json:"schedule"`
	ScheduleId int           `json:"schedule_id"`
	Trigger    string        `json:"trigger"`
	CreateTime dto.LocalTime `json:"createTime"`
	UpdateTime dto.LocalTime `json:"updateTime"`
	model.Model
}

type ProcessorCache map[string]interface{}

func (proc *Processor) GetById(id string) error {
	err := proc.Orm.Where(Processor{Id: id}).First(proc).Error
	return err
}

func (proc *Processor) GetOne(q *query.ProcessorGetReq) error {
	where := dto.MakeCondition(*q)
	err := proc.FindOne(proc, where)
	return err
}

func (proc *Processor) FindEnableScheduled() (result []*Processor, err error) {
	list := make([]*Processor, 0)
	err = proc.Orm.Table("sys_rule_processor p").Select("p.*").
		Joins("left join sys_rule r on p.rule_id = r.id").
		Where("r.status = ? and p.trigger = ?", "enable", constant.RuleTriggerSchedule).
		Find(&list).Error
	return list, err
}

func (proc *Processor) FindScheduledByRuleId(ruleId string) (result []*Processor, err error) {
	list := make([]*Processor, 0)
	err = proc.Orm.Model(proc).Where(Processor{RuleId: ruleId, Trigger: constant.RuleTriggerSchedule}).Find(&list).Error
	return list, err
}

// FindPage 获取处理器列表
func (proc *Processor) FindPage(q *query.ProcessorGetReq) (result interface{}, count int64, err error) {
	list := make([]*Processor, 0)
	where := dto.MakeCondition(*q)

	err = proc.Orm.Model(proc).Scopes(
		where,
		dto.OrderDest("create_time", true, q.OrderBy),
		dto.Paginate(q.GetPageSize(), q.GetPageIndex()),
	).Find(&list).Limit(-1).Offset(-1).Count(&count).Error

	return list, count, nil
}

// FindChainsByRule 按规则id获取处理器列表,并按照先后顺序存入切片
func (proc *Processor) FindChainsByRule(ruleId string) (result []ProcessorCache) {
	list := make([]*Processor, 0)
	_ = proc.Orm.Model(proc).Where(Processor{RuleId: ruleId}).Find(&list).Error

	if len(list) == 0 {
		return nil
	}

	// 将processor存入以id为key的map
	processorMap := make(map[string]*Processor)
	for _, p := range list {
		processorMap[p.Id] = p
	}

	// 找到第一个节点
	orderList := make([]ProcessorCache, 0)
	first := proc.findFirstProcessor(list)

	cache := map[string]interface{}{
		"id":      first.Id,
		"script":  first.Script,
		"trigger": first.Trigger,
	}

	if first != nil {
		orderList = append(orderList, cache)
	}

	// 递归组成规则链
	orderList = proc.findNextProcessor(first, processorMap, orderList)

	return orderList
}

func (proc *Processor) findFirstProcessor(list []*Processor) (first *Processor) {
	for _, p := range list {
		if p.Previous == "no" {
			return p
		}
	}
	return nil
}

// 递归组成规则链
func (proc *Processor) findNextProcessor(current *Processor, processorMap map[string]*Processor, orderList []ProcessorCache) (result []ProcessorCache) {
	if current == nil || current.Next == "no" || current.Next == "" {
		return orderList
	}

	next, ok := processorMap[current.Next]
	if !ok {
		return orderList
	}

	cache := map[string]interface{}{
		"id":      next.Id,
		"script":  next.Script,
		"trigger": next.Trigger,
	}

	orderList = append(orderList, cache)
	proc.findNextProcessor(next, processorMap, orderList)
	return orderList
}

func (proc *Processor) DisableScheduled(id string) {
	_ = proc.Orm.Model(proc).Where(Processor{Id: id}).First(proc).Error
	_ = proc.SaveScheduleId(0)

	env.Log.Debugf("remove processor %s with schedule id %d (%s)", proc.Id, proc.ScheduleId, proc.Schedule)
}

func (proc *Processor) SaveScheduleId(scheduleId int) error {
	update := map[string]interface{}{"schedule_id": scheduleId}
	return proc.Orm.Model(proc).Updates(update).Error
}

func (proc *Processor) AddProcessor() error {
	err := proc.Add(proc)
	proc.updateEnabledRuleCache(proc.RuleId)

	return err
}

func (proc *Processor) Modify(model interface{}) error {
	err := proc.Mod(model)
	_ = proc.GetById(proc.Id)
	proc.updateEnabledRuleCache(proc.RuleId)

	return err
}

func (proc *Processor) Remove() error {
	err := proc.Delete(proc)
	_ = proc.GetById(proc.Id)
	proc.updateEnabledRuleCache(proc.RuleId)

	return err
}

func (proc *Processor) updateEnabledRuleCache(ruleId string) {
	rule := &Rule{}
	rule.Orm = env.Db
	_ = rule.GetById(ruleId)

	if rule.Status == constant.RuleEnable {
		rule.setRuleCache()
	}
}

func (proc *Processor) RemoveByRuleId() {
	_ = proc.DeleteByConds(proc, Processor{RuleId: proc.RuleId})
}

func (proc *Processor) TableName() string {
	return "sys_rule_processor"
}

func (proc *Processor) GetId() interface{} {
	return proc.Id
}
